from django.db import transaction
from rest_framework import serializers

from goods.models import SKU,GoodsCategory,SPU, SPUSpecification, SpecificationOption, SKUSpecification

# 为了重写specs,再建一个序列化器
class SKUSpecificationSerializer(serializers.Serializer):
    spec_id = serializers.IntegerField()
    option_id = serializers.IntegerField()


# 写一个商品序列化器，因为是商品，所以，基于sku模型表
class SKUViewSetSerializer(serializers.ModelSerializer):

    # #1,重写分类
    category = serializers.StringRelatedField(read_only=True)
    category_id = serializers.IntegerField()

    #2,spu重写
    spu = serializers.StringRelatedField(read_only=True)
    spu_id = serializers.IntegerField()

    # 3,specs重写
    specs = SKUSpecificationSerializer(read_only=True, many=True)

    class Meta:
        model = SKU
        fields = "__all__"

        # 3,重写create方法,保存sku和对应的规格
        @transaction.atomic #使用事务步骤1，导入transaction，然后点atomic，开启事物
        def create(self, validated_data):

            # 使用事务步骤2，TOOD 设置保存点
            # 使用事务目的：在添加sku的时候，保证对应的spec的规格同时保存成功或失败。
            sid = transaction.savepoint() #点一个保存点

            # 试一个，如果条件都通过，才能入库
            try:
                # 1,创建sku对象入库
                sku = SKU.objects.create(**validated_data)

                # 2,创建规格信息入库
                specs = self.context["request"].data["specs"] #打断点，调试而来
                for spec_dict in specs: #遍历
                    SKUSpecification.objects.create(sku_id=sku.id, spec_id=spec_dict["spec_id"],option_id=spec_dict["option_id"])
                    #如果说sku的id，和spec_id，以及option_id，都相同，那就创建入库保存
            #通不过就抓错误
            except Exception as e:
                # TOOD 回滚
                transaction.savepoint_rollback(sid)  # 如果出错，说明spec的信息没有一起入库成功或者失败，则事务回滚

                raise serializers.ValidationError("保存失败") #返回一个验证错误的错误

            else:
                # 3,返回响应
                transaction.savepoint_commit(sid) #还不行，提交事务，
                return sku # 返回最开始的sku


        # 4,重写update方法,更新sku中的规格到数据库中
        @transaction.atomic
        def update(self, instance, validated_data):
            """由于原来的序列化器，只是更新其关联的模型表的数据，无法更新修改其关联的表，所以修改只能修改基本信息，还有不少信息无法修改，
            因此，必须要重写update方法"""

            # TODO 设置保存点
            sid = transaction.savepoint()

            try:
                # 1,更新sku其他信息(标题,价格,...)
                SKU.objects.filter(id=instance.id).update(**validated_data)

                #1.1 删除以前所有的规格，因为传过来的数目与原先不一样，无法更新，所以，删掉再入库
                [spec.delete() for spec in instance.specs.all()] #列表推导式

                # 2,更新规格信息入库
                specs = self.context["request"].data["specs"]
                for spec_dict in specs:
                    SKUSpecification.objects.create(sku_id=instance.id, spec_id=spec_dict['spec_id'],option_id=spec_dict["option_id"])# 本来是fileter...update，现在是create...没有update,直接条件符合，入库
            except Exception as e:
                transaction.savepoint_rollback(sid)  # TODO 回滚
                raise serializers.ValidationError("保存失败")
            else:

                # 3,返回响应,
                transaction.savepoint_commit(sid)  # TODO 提交
                return SKU.objects.get(id=instance.id)

# 新增商品是分类的显示，所以基于商品分类模型生成
class SKUCategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = GoodsCategory
        fields = ("id","name")

# 将spu的数据展示在sku的添加页面
class SKUGoodSimpleSerializer(serializers.ModelSerializer):
    class Meta:
        model = SPU
        fields = ("id","name")


#为了重写options，新定义一个序列化器,选项序列化器
class SpecificationOptionSerializer(serializers.ModelSerializer):
    class Meta:
        model = SpecificationOption #导入选项规格模型表，基于此生成
        fields = ('id','value') # 因为前端只要这两个


#  sku的商品规格序列化器
class SKUGoodSpecsSerializer(serializers.ModelSerializer):

    # # 重写spu，不重写的情况下，默认以整数形式展示，但现在的名字是有文字的，所以要转成字符串
    spu = serializers.StringRelatedField(read_only=True)
    spu_id =serializers.IntegerField() # 把下划线id也补上，为了前端取数方便，本来只有一个id,现在是有名字，加id
    #
    # 重写option
    options = SpecificationOptionSerializer(read_only=True,many=True) # 调用自己写的选项序列化器，完成重写，因为是在一方里写序列化器，所以加many。在多方里面写，不用加.
    # 因为SPUS规格表与specs选项表，是一对多的关系，而此时，是在商品规格下面写，所以是在一方里写

    class Meta:
        model = SPUSpecification
        fields = "__all__"